package com.maodou.core.network

import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flatMapConcat
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.transform

/**
 * Flow-based API helpers that mimic RxJava-like chaining while keeping
 * strong typing with NetworkResult.
 */

// 发起一次网络调用并发出单个结果（Success 或 Failure）
inline fun <T> flowApiCall(
    dispatcher: CoroutineDispatcher = Dispatchers.IO,
    crossinline block: suspend () -> T
): Flow<NetworkResult<T>> = flow {
    emit(safeApiCall(dispatcher) { block() })
}.flowOn(dispatcher)

// 将成功结果转换为另一种类型，失败原样透传
inline fun <T, R> Flow<NetworkResult<T>>.mapSuccess(crossinline transform: (T) -> R): Flow<NetworkResult<R>> =
    map { result -> result.mapSuccess(transform) }

// 将成功结果展开为另一个 NetworkResult，失败原样透传
inline fun <T, R> Flow<NetworkResult<T>>.flatMapSuccess(
    crossinline transform: suspend (T) -> NetworkResult<R>
): Flow<NetworkResult<R>> = map { result ->
    when (result) {
        is NetworkResult.Success -> transform(result.data)
        is NetworkResult.Failure -> NetworkResult.Failure(result.error)
    }
}

// 以最新值展开成功流（如根据上游结果触发下一次请求）
@OptIn(ExperimentalCoroutinesApi::class)
inline fun <T, R> Flow<NetworkResult<T>>.flatMapLatestSuccess(
    crossinline transform: suspend (T) -> Flow<NetworkResult<R>>
): Flow<NetworkResult<R>> = flatMapLatest { result ->
    when (result) {
        is NetworkResult.Success -> transform(result.data)
        is NetworkResult.Failure -> flow { emit(NetworkResult.Failure(result.error)) }
    }
}

// 以串行方式展开成功流（严格保持顺序）
@OptIn(ExperimentalCoroutinesApi::class)
inline fun <T, R> Flow<NetworkResult<T>>.flatMapConcatSuccess(
    crossinline transform: suspend (T) -> Flow<NetworkResult<R>>
): Flow<NetworkResult<R>> = flatMapConcat { result ->
    when (result) {
        is NetworkResult.Success -> transform(result.data)
        is NetworkResult.Failure -> flow { emit(NetworkResult.Failure(result.error)) }
    }
}

// 仅发出成功的数据流，便于与标准 Flow 操作符配合
fun <T> Flow<NetworkResult<T>>.onlySuccess(): Flow<T> =
    transform { result -> if (result is NetworkResult.Success) emit(result.data) }

// 仅发出错误流，便于集中告警或埋点
fun <T> Flow<NetworkResult<T>>.onlyFailure(): Flow<NetworkError> =
    transform { result -> if (result is NetworkResult.Failure) emit(result.error) }

// 便捷的错误映射：将异常统一捕获为 Failure(Unknown)
fun <T> Flow<NetworkResult<T>>.catchAsFailure(): Flow<NetworkResult<T>> =
    catch { e -> emit(NetworkResult.Failure(NetworkError.Unknown(e))) }

// 在链路中对成功/失败分别处理副作用（不改变数据）
inline fun <T> Flow<NetworkResult<T>>.onSuccess(
    crossinline action: (T) -> Unit
): Flow<NetworkResult<T>> = map { it.onSuccess(action) }

inline fun <T> Flow<NetworkResult<T>>.onFailure(
    crossinline action: (NetworkError) -> Unit
): Flow<NetworkResult<T>> = map { it.onFailure(action) }

// 按需在失败时兜底为成功值（如提供缓存）
inline fun <T> Flow<NetworkResult<T>>.recover(
    crossinline transform: (NetworkError) -> T
): Flow<NetworkResult<T>> = map { it.recover(transform) }

// 将 Failure 映射为另一种错误，便于业务统一归类
inline fun <T> Flow<NetworkResult<T>>.mapFailure(
    crossinline mapper: (NetworkError) -> NetworkError
): Flow<NetworkResult<T>> = map { result ->
    when (result) {
        is NetworkResult.Success -> result
        is NetworkResult.Failure -> NetworkResult.Failure(mapper(result.error))
    }
}

// —— 以下为 Envelope 友好 API，便于与后端统一响应结构对接 ——

// 单体数据：ApiEnvelope<T> → NetworkResult<T>
inline fun <T> flowApiEnvelopeCall(
    dispatcher: CoroutineDispatcher = Dispatchers.IO,
    crossinline block: suspend () -> ApiEnvelope<T>
): Flow<NetworkResult<T>> = flow {
    when (val result = safeApiCall(dispatcher) { block() }) {
        is NetworkResult.Success -> emit(result.data.toResult())
        is NetworkResult.Failure -> emit(result)
    }
}.flowOn(dispatcher)

// 可选数据：ApiEnvelope<T?> → NetworkResult<T?>（success=true 即成功，即使 data 为空）
inline fun <T> flowApiEnvelopeOptionalCall(
    dispatcher: CoroutineDispatcher = Dispatchers.IO,
    crossinline block: suspend () -> ApiEnvelope<T?>
): Flow<NetworkResult<T?>> = flow {
    when (val result = safeApiCall(dispatcher) { block() }) {
        is NetworkResult.Success -> emit(result.data.toOptionalResult())
        is NetworkResult.Failure -> emit(result)
    }
}.flowOn(dispatcher)

// 无数据：ApiEnvelope<*> → NetworkResult<Unit>
inline fun flowApiEnvelopeUnitCall(
    dispatcher: CoroutineDispatcher = Dispatchers.IO,
    crossinline block: suspend () -> ApiEnvelope<*>
): Flow<NetworkResult<Unit>> = flow {
    when (val result = safeApiCall(dispatcher) { block() }) {
        is NetworkResult.Success -> emit(result.data.toUnitResult())
        is NetworkResult.Failure -> emit(result)
    }
}.flowOn(dispatcher)

// 容错（宽松）：ApiEnvelope<T> → NetworkResult<T?>
// 只要 success=true 就算成功；若 data 为空，则以 Success(null) 形式返回。
//inline fun <T> flowApiEnvelopeLenientCall(
//    dispatcher: CoroutineDispatcher = Dispatchers.IO,
//    crossinline block: suspend () -> ApiEnvelope<T>
//): Flow<NetworkResult<T?>> = flow {
//    when (val result = safeApiCall(dispatcher) { block() }) {
//        is NetworkResult.Success -> emit(result.data.to)
//        is NetworkResult.Failure -> emit(result)
//    }
//}.flowOn(dispatcher)

// 分页完整数据：ApiEnvelope<PageEnvelope<T>> → NetworkResult<PageEnvelope<T>>
inline fun <T> flowApiPageCall(
    dispatcher: CoroutineDispatcher = Dispatchers.IO,
    crossinline block: suspend () -> ApiEnvelope<PageEnvelope<T>>
): Flow<NetworkResult<PageEnvelope<T>>> = flow {
    when (val result = safeApiCall(dispatcher) { block() }) {
        is NetworkResult.Success -> emit(result.data.toPageResult())
        is NetworkResult.Failure -> emit(result)
    }
}.flowOn(dispatcher)

// 分页仅 records：ApiEnvelope<PageEnvelope<T>> → NetworkResult<List<T>>
inline fun <T> flowApiRecordsCall(
    dispatcher: CoroutineDispatcher = Dispatchers.IO,
    crossinline block: suspend () -> ApiEnvelope<PageEnvelope<T>>
): Flow<NetworkResult<List<T>>> = flow {
    when (val result = safeApiCall(dispatcher) { block() }) {
        is NetworkResult.Success -> emit(result.data.toRecordsResult())
        is NetworkResult.Failure -> emit(result)
    }
}.flowOn(dispatcher)

// 可重试的调用：对满足条件的错误（默认网络错误与 5xx）进行指数退避重试
inline fun <T> retryingApiCall(
    dispatcher: CoroutineDispatcher = Dispatchers.IO,
    maxRetries: Int = 2,
    initialDelayMillis: Long = 500,
    factor: Double = 2.0,
    crossinline retryOn: (NetworkError) -> Boolean = { err ->
        err is NetworkError.Network || (err is NetworkError.Http && err.code >= 500)
    },
    crossinline block: suspend () -> T
): Flow<NetworkResult<T>> = flow {
    var attempt = 0
    var delayMs = initialDelayMillis
    while (true) {
        val result = safeApiCall(dispatcher) { block() }
        if (result is NetworkResult.Success) {
            emit(result)
            return@flow
        }
        val error = (result as NetworkResult.Failure).error
        if (attempt >= maxRetries || !retryOn(error)) {
            emit(result)
            return@flow
        }
        attempt++
        delay(delayMs)
        delayMs = (delayMs * factor).toLong().coerceAtLeast(1L)
    }
}.flowOn(dispatcher)

// 对 401/403 进行一次 Token 刷新后重试原请求（refresh 返回 true 表示刷新成功）
inline fun <T> authRetryCall(
    dispatcher: CoroutineDispatcher = Dispatchers.IO,
    crossinline refresh: suspend () -> Boolean,
    crossinline block: suspend () -> T
): Flow<NetworkResult<T>> = flow {
    when (val first = safeApiCall(dispatcher) { block() }) {
        is NetworkResult.Success -> emit(first)
        is NetworkResult.Failure -> {
            val error = first.error
            if (error is NetworkError.Http && (error.code == 401 || error.code == 403)) {
                val refreshed = runCatching { refresh() }.getOrDefault(false)
                if (refreshed) {
                    emit(safeApiCall(dispatcher) { block() })
                } else {
                    emit(first)
                }
            } else {
                emit(first)
            }
        }
    }
}.flowOn(dispatcher)